package main

import (
	"fmt"
	"sync"
	"time"
)

type ChapterGoRoutine struct {
}

func main() {
	// 第1种情况
	//go wangzhaojun()
	//fmt.Println("main 函数运行完成")

	go wangzhaojun()

	//匿名函数添加go
	go func() {
		fmt.Println("打印匿名函数")
	}()

	//在第1种情况上添加睡眠
	time.Sleep(1000 * time.Millisecond)
	fmt.Println("main 函数运行完成")
	//打印奇偶数
	this := ChapterGoRoutine{}
	go this.printOdd()
	go this.printEven()
	time.Sleep(5 * time.Second)
	fmt.Println("main 等待奇偶数打印完成")
	//在多个goroutine下,可以调用 runtime.Gosched() 交出控制权
	//维护cpu核心数量 runtime.GOMAXPROCS() ,参数为 逻辑cpu的数量,
	//逻辑cpu数量有3种数量,<1 不改变CPU数量 , =1 单核执行 , >1 多核并发执行
	//channel 信道的初始化
	ch1 := make(chan int)
	ch2 := make(chan interface{})
	type TestChannelInit struct {
	}
	ch3 := make(chan *TestChannelInit)
	fmt.Printf("ch1  %T  , ch2 %T , ch3 %T \n ", ch1, ch2, ch3)
	ch4 := make(chan string)
	go this.sendData(ch4)
	//第1种遍历方式
	//for {
	//	data := <-ch4
	//	if data == "" {
	//		break
	//	}
	//	fmt.Println("遍历channel方式1, 从通道中接收到的数据:", data)
	//}

	//遍历channel方式2
	//for {
	//	data, ok := <-ch4
	//	if !ok{
	//		break
	//	}
	//	fmt.Println("遍历channel方式2, 从通道中接收到的数据:", data)
	//}
	//遍历channel 方式3,for range 会自动判断 channel 是否自动关闭
	for data := range ch4 {
		fmt.Println("遍历channel方式3, 从通道中接收到的数据:", data)
	}
	// 利用 channel 的阻塞功能
	ch5 := make(chan int)
	fmt.Println("非缓冲通道ch5 长度和容量", len(ch5), cap(ch5))
	ch6 := make(chan bool)
	go func() {
		fmt.Printf("ch5获取到值之前,当前时间 %v \n ", time.Now())
		data, ok := <-ch5
		if ok {
			fmt.Printf("匿名函数中获取到了ch5信道传送的数据,数据是 %v: , 当前时间 %v \n ", data, time.Now())
			//收到数据后传送个数据给主函数
			ch6 <- true
		}

	}()
	fmt.Printf("赋值给ch5之前,当前时间 %v \n ", time.Now())
	ch5 <- 10
	fmt.Printf("赋值给ch5之后,当前时间 %v \n ", time.Now())
	data, ok := <-ch6
	if ok {
		fmt.Printf("主函数中获取到了ch6信道传送的数据,数据是: %v ,当前时间 %v \n ", data, time.Now())
	}
	defer close(ch5)
	defer close(ch6)
	//往关闭的channel中写入数据,会提示 panic: send on close channel
	//缓冲channel
	ch7 := make(chan string, 5)
	go this.sendData(ch7)
	for data := range ch7 {
		fmt.Println("ch7接收到的数据:", data)
	}

	ch8 := make(chan bool)
	go func() {
		defer close(ch8)
		fmt.Println("ch8 休息3秒")
		time.Sleep(3 * time.Second)
		ch8 <- true
	}()
	//ch8阻塞住 主函数
	fmt.Printf("因为ch8 主函数被堵塞住了 ,当前时间 %v \n ", time.Now())
	<-ch8
	fmt.Printf("ch8阻塞结束,当前时间 %v \n ", time.Now())
	// channel默认是读写双向的,在使用时候可以加限制,只读或者只写,
	// 只写  ch<- xxx  ,只读  xxx <-ch

	//timer
	timer := time.NewTimer(5 * time.Second)
	fmt.Println("time.Now() == ", time.Now())
	dataC := <-timer.C
	fmt.Printf("dataC 类型 %T , 值是 %v \n", dataC, dataC)
	//timer 的 after 用法
	ch9 := time.After(5 * time.Second)
	fmt.Printf("ch9 time.Now() == %v , ch9的类型 %T \n", time.Now(), ch9)
	dataC = <-ch9
	fmt.Printf("ch9 dataC 类型 %T , 值是 %v \n", dataC, dataC)
	//select 语句
	ch10 := make(chan int)
	ch11 := make(chan int)
	select {
	case data := <-ch10:
		fmt.Println("ch10 的数据:", data)

	case data := <-ch11:
		fmt.Println("ch11 的数据:", data)
	default:
		fmt.Println("default 的数据:", 0)
	}

	// 等待组,类似 countdownlatch
	var wg sync.WaitGroup
	wg.Add(3)
	go this.testSync(&wg, 1)
	go this.testSync(&wg, 2)
	go this.testSync(&wg, 3)
	// wait 进入阻塞状态,等待计数器为0后解除阻塞
	fmt.Println("sync.WaitGroup 主函数堵塞开始")
	wg.Wait()
	fmt.Println("sync.WaitGroup 主函数结束")

	//互斥锁 mutex
	wg.Add(4)
	go this.sellTicket(&wg, "1号售票窗口")
	go this.sellTicket(&wg, "2号售票窗口")
	go this.sellTicket(&wg, "3号售票窗口")
	go this.sellTicket(&wg, "4号售票窗口")
	wg.Wait()
	fmt.Println("所有车票已售空")

	// 读写互斥锁
	rwMutex := sync.RWMutex{}
	//写锁定
	rwMutex.Lock()
	//写解锁
	rwMutex.Unlock()
	//读锁定
	rwMutex.RLock()
	//读解锁
	rwMutex.RUnlock()

	// 条件变量
	cond := sync.Cond{L: &mutex}
	condition := false
	go func() {
		fmt.Printf("子协程未锁定前, 当前时间 %v \n ", time.Now())
		time.Sleep(2 * time.Second)
		cond.L.Lock()
		fmt.Printf("子协程已锁定,更改condition并发送通知, 当前时间 %v \n ", time.Now())
		condition = true
		// 发送通知后并不会立即解锁,要等待释放锁后等待的协程才可以执行
		cond.Signal()
		fmt.Printf("子协程通知完后继续执行, 当前时间 %v \n ", time.Now())
		time.Sleep(5 * time.Second)
		cond.L.Unlock()
		fmt.Printf("子协程解锁, 当前时间 %v \n ", time.Now())
	}()
	cond.L.Lock()
	fmt.Printf("主函数已锁定 , 当前时间 %v \n ", time.Now())
	if !condition {
		fmt.Printf("主函数等待唤醒, 当前时间 %v \n ", time.Now())
		//wait 方法执行后会释放继续执行的权力,一直等待通知并且解锁后才会被唤醒
		cond.Wait()
		fmt.Printf("主函数被唤醒, 当前时间 %v \n ", time.Now())
	}
	fmt.Printf("主函数唤醒后继续执行, 当前时间 %v \n ", time.Now())
	cond.L.Unlock()
	fmt.Printf("主函数解锁, 当前时间 %v \n ", time.Now())
}

var mutex = sync.Mutex{}
var tickets = 60

func (c *ChapterGoRoutine) sellTicket(w *sync.WaitGroup, info string) {
	defer w.Done()
	for {
		mutex.Lock()
		if tickets < 0 {
			mutex.Unlock()
			break
		} else {
			fmt.Println(info, "售出1张票")
			time.Sleep(50 * time.Millisecond)
			tickets--
			mutex.Unlock()
		}

	}

}

func (c *ChapterGoRoutine) testSync(w *sync.WaitGroup, num int) {
	time.Sleep(3 * time.Second)
	fmt.Printf("testSync 第 %d 个结束 \n", num)
	w.Done()
}

func (c *ChapterGoRoutine) sendData(ch chan string) {
	defer close(ch)
	for i := 0; i < 3; i++ {
		fmt.Println("信道中放入数据", i)
		ch <- fmt.Sprintf("发送数据 %d \n ", i)
	}
	fmt.Println("发送数据结束")
}

func wangzhaojun() {
	fmt.Println("此刻正在听任素汐的王招君")
}
func (c *ChapterGoRoutine) printOdd() {
	for i := 1; i < 10; i = i + 2 {
		fmt.Println(i)
		time.Sleep(10 * time.Millisecond)
	}
}

func (c *ChapterGoRoutine) printEven() {
	for i := 2; i <= 10; i = i + 2 {
		fmt.Println(i)
		time.Sleep(10 * time.Millisecond)
	}
}
